<?php
// +---------------------------------------------------------------------+
// | OneBase    | [ WE CAN DO IT JUST THINK ]                            |
// +---------------------------------------------------------------------+
// | Licensed   | http://www.apache.org/licenses/LICENSE-2.0 )           |
// +---------------------------------------------------------------------+
// | Author     | Bigotry <3162875@qq.com>                               |
// +---------------------------------------------------------------------+
// | Repository | https://gitee.com/Bigotry/OneBase                      |
// +---------------------------------------------------------------------+

// 扩展函数文件，系统研发过程中需要的函数建议放在此处，与框架相关函数分离

// 兼容 TP5 风格的 config() 助手
if (!function_exists('config')) {
    function config($name = null, $value = null, $default = null)
    {
        if (is_null($name)) {
            return \think\facade\Config::get();
        }

        // 兼容 TP5：config(['a'=>1,'b'=>2]) 批量设置
        if (is_array($name) && is_null($value)) {
            \think\facade\Config::set($name);
            return $name;
        }

        // 读取配置（支持“点”语法）
        if (is_null($value)) {
            // 修复：不含点的一级配置名（可能是字符串），避免触发 Config::pull() 的 array 返回类型约束
            if (is_string($name) && strpos($name, '.') === false) {
                $all      = \think\facade\Config::get(); // 获取所有配置数组
                $keyLower = strtolower($name);
                $ret      = $all[$keyLower] ?? $all[$name] ?? null;
                return $ret ?? $default;
            }

            // 含点的多级配置按原逻辑处理
            $ret = \think\facade\Config::get($name);
            return $ret ?? $default;
        }

        // 设置配置（TP8 的 Config::set 仅支持数组，兼容 TP5 的 set('a', 1) 用数组包裹）
        if (is_string($name)) {
            \think\facade\Config::set([$name => $value]);
            return $value;
        }

        // 若传入为数组且有第二参数，仍以批量设置方式处理
        if (is_array($name)) {
            \think\facade\Config::set($name);
            return $name;
        }

        return null;
    }
}

// 兼容 TP5 风格的 cache() 助手
if (!function_exists('cache')) {
    function cache($name, $value = null, $expire = null)
    {
        if (is_null($value)) {
            return \think\facade\Cache::get($name);
        }
        if ($value === false) {
            return \think\facade\Cache::delete($name);
        }
        return \think\facade\Cache::set($name, $value, $expire ?? 0);
    }
}

// 兼容 TP5 风格的 url() 助手（尽量构建，失败则回退原值）
if (!function_exists('url')) {
    function url($url = '', $vars = [], $suffix = true, $domain = false)
    {
        try {
            $builder = \think\facade\Route::buildUrl($url, $vars);
            if (method_exists($builder, 'suffix')) {
                $builder = $builder->suffix($suffix);
            }
            if (method_exists($builder, 'domain')) {
                $builder = $builder->domain($domain);
            }
            return $builder->build();
        } catch (\Throwable $e) {
            return $url;
        }
    }
}

// 兼容 TP5 风格的 model() 助手：支持当前应用优先、显式模块、common 回退、复杂层级路径
if (!function_exists('model')) {
    /**
     * 获取模型/逻辑/服务等层的实例
     * $name 可为：
     *  - 类名：'Config'、'Demo'
     *  - 带路径：'pay\\driver\\Alipay'、'wxpay\\WxPayPubHelper'
     *  - 显式模块：'common\\Config'、'admin\\User'
     * $layer 可为：
     *  - 'model'（默认）、'logic'、'service'、'validate' 或更深层 'service\\pay\\driver'
     */
    function model($name, $layer = null)
    {
        $app   = app();
        $layer = $layer ?: 'model';

        // 统一分隔符
        $layer = str_replace(['/', '.', '\\\\'], '\\', $layer);
        $name  = str_replace(['/', '.'], '\\', $name);

        $parts = explode('\\', $name);
        $knownModules = ['common', 'admin', 'api', 'index', 'install', 'queue', 'demo'];

        $explicitModule = '';
        if (count($parts) > 1 && in_array(strtolower($parts[0]), $knownModules, true)) {
            $explicitModule = strtolower(array_shift($parts));
        }

        // 构建类名
        if ($explicitModule === '') {
            // 当前应用优先
            $relative = implode('\\', $parts);
            $class    = $app->parseClass($layer, $relative);

            if (!class_exists($class)) {
                // 回退到 common 模块
                $class = 'app\\common\\' . trim($layer, '\\') . '\\' . $relative;
                $class = rtrim($class, '\\');
            }
        } else {
            // 显式模块：手动拼接，处理末段类名为 StudlyCase
            $basename = array_pop($parts);
            $path     = $parts ? implode('\\', $parts) . '\\' : '';
            $studly   = str_replace(' ', '', ucwords(str_replace(['_', '-'], ' ', $basename)));

            $class = 'app\\' . $explicitModule . '\\' . trim($layer, '\\') . '\\' . $path . $studly;
        }

        if (!class_exists($class)) {
            throw new \RuntimeException('类未找到: ' . $class);
        }

        // 优先使用容器实例化（支持依赖注入），失败则常规 new
        try {
            return $app->make($class);
        } catch (\Throwable $e) {
            return new $class();
        }
    }
}

// 新增：安全读取“文本型配置”的助手，供模板标签 {:config_value(...)} 使用
if (!function_exists('config_value')) {
    /**
     * 安全读取文本型配置项
     * - 若返回为数组且包含 'value' 键，则取其 'value'
     * - 若返回为数组但不包含 'value'，则返回默认值或空字符串，避免模板输出时报错
     * author: 王勇程 <laughskydragon@qq.com>
     */
    function config_value($name, $default = '')
    {
        $ret = config($name, null, $default);

        if (is_array($ret)) {
            if (array_key_exists('value', $ret) && (is_string($ret['value']) || is_numeric($ret['value']))) {
                return (string)$ret['value'];
            }
            // 兜底：数组不可直接输出，改为返回默认值或空字符串
            return is_string($default) || is_numeric($default) ? (string)$default : '';
        }

        // 标量直接返回字符串
        return is_null($ret) ? (is_string($default) ? $default : '') : (string)$ret;
    }
}

// 新增：模板安全过滤器 e()，避免对 null 值调用 htmlentities 触发弃用警告
if (!function_exists('e')) {
    /**
     * e() 过滤器：将输入安全转为字符串并进行 HTML 实体转义
     * author: 王勇程 <laughskydragon@qq.com>
     */
    function e($value)
    {
        if ($value === null || is_array($value) || is_object($value)) {
            $value = '';
        }
        $value = (string)$value;
        return htmlentities($value, ENT_QUOTES, 'UTF-8');
    }
}

// 新增：公共函数 normalize_where
if (!function_exists('normalize_where')) {
    /**
     * 公共函数：归一化 where 条件
     * 用途：将 TP5 风格的关联数组条件统一转换为 TP8 兼容的三元数组，避免 ['neq', -1] 等被误解析为 IN
     * 支持示例：
     * - ['status' => ['neq', -1]]                    => [['status','<>',-1]]
     * - ['title'  => ['like','%abc%']]              => [['title','like','%abc%']]
     * - ['id'     => ['in',[1,2,3]]]                => [['id','in',[1,2,3]]]
     * - ['ctime'  => ['between',[1700,1800]]]       => [['ctime','between',[1700,1800]]]
     * - ['status' => 1]                             => [['status','=',1]]
     * - [['status','neq',-1], ['title','like','%t%']] => 操作符映射后原样保留
     * 适用场景：直接使用 Db::name()/Db::table() 或底层 Query，在不经过 ModelBase 的查询入口时，优先调用该函数
     * author: 王勇程 <laughskydragon@qq.com>
     */
    function normalize_where($where)
    {
        if ($where instanceof \Closure) { return $where; }
        if (!is_array($where) || empty($where)) { return $where; }

        $opMap = [
            'eq' => '=', 'neq' => '<>', 'gt' => '>', 'lt' => '<',
            'egt' => '>=', 'elt' => '<=', 'like' => 'like', 'not like' => 'not like',
            'in' => 'in', 'not in' => 'not in', 'between' => 'between', 'not between' => 'not between',
        ];

        $normalized = [];
        $is_list = array_keys($where) === range(0, count($where) - 1);

        if ($is_list) {
            // 列表数组：如 [['field','op',value]] 或 ['field',['op',value]]
            foreach ($where as $item) {
                if ($item instanceof \Closure) { $normalized[] = $item; continue; }
                if (!is_array($item) || empty($item)) { continue; }

                // ['field', 'op', value]
                if (isset($item[0]) && isset($item[1]) && !is_array($item[1])) {
                    $field = $item[0];
                    $op    = strtolower((string)$item[1]);
                    $op    = $opMap[$op] ?? $item[1];
                    $value = $item[2] ?? null;
                    $normalized[] = [$field, $op, $value];
                    continue;
                }

                // ['field', ['op', value]]
                if (isset($item[0]) && isset($item[1]) && is_array($item[1]) && count($item[1]) >= 1) {
                    $field = $item[0];
                    $opRaw = (string)($item[1][0] ?? '');
                    $op    = $opMap[strtolower($opRaw)] ?? $opRaw;
                    $value = $item[1][1] ?? null;
                    $normalized[] = [$field, $op, $value];
                    continue;
                }

                // 兜底：无法识别的项直接保留（交给 ORM 自行处理）
                $normalized[] = $item;
            }
        } else {
            // 关联数组：如 ['field' => ['op', value]] 或 ['field' => value]
            foreach ($where as $field => $value) {
                if ($value instanceof \Closure) { $normalized[] = $value; continue; }

                if (is_array($value) && !empty($value)) {
                    // ['op', value] 或 ['between',[a,b]] 等
                    if (is_string($value[0] ?? null)) {
                        $opRaw = strtolower($value[0]);
                        $op    = $opMap[$opRaw] ?? $opRaw;
                        $val1  = $value[1] ?? null;

                        // between/not between 允许两个值数组
                        if (($op === 'between' || $op === 'not between') && is_array($val1) && count($val1) === 2) {
                            $normalized[] = [$field, $op, $val1];
                        } else {
                            $normalized[] = [$field, $op, $val1];
                        }
                        continue;
                    }

                    // 没有显式操作符但为“列表数组且全为标量”的情况，推断为 IN
                    $is_list_values = array_keys($value) === range(0, count($value) - 1);
                    $all_scalar = $is_list_values && !array_filter($value, function($v) { return is_array($v) || is_object($v); });
                    if ($is_list_values && $all_scalar) {
                        $normalized[] = [$field, 'in', $value];
                        continue;
                    }

                    // 兜底：无法明确识别操作符，保留原样（减少误判风险）
                    $normalized[] = [$field, 'in', $value];
                    continue;
                }

                // 标量：等值
                $normalized[] = [$field, '=', $value];
            }
        }

        return $normalized;
    }
}

// 新增：ThinkPHP5兼容函数 - debug() 性能监控函数
if (!function_exists('debug')) {
    /**
     * ThinkPHP5兼容函数：性能监控和执行时间计算
     * 用于记录时间点标记和计算执行时间，兼容从TP5升级到TP8的项目
     * 
     * 使用方式：
     * - debug('start') - 记录时间点标记
     * - debug('start', 'end') - 计算两个时间点的差值并返回执行时间（秒）
     * - debug('start', 'end', 'm') - 计算两个时间点的内存使用差值
     * 
     * @param string $start 开始标记名称
     * @param string $end 结束标记名称，为空时表示记录时间点
     * @param mixed $dec 小数位数或'm'表示内存计算，默认6位小数
     * @return mixed 无返回值（记录时）或执行时间/内存差值
     * 
     * author: 王勇程 <laughskydragon@qq.com>
     */
    function debug($start, $end = '', $dec = 6)
    {
        static $info = [];  // 存储时间标记
        static $mem = [];   // 存储内存标记
        
        if ('' == $end) {
            // 记录时间点和内存使用情况
            $info[$start] = microtime(true);
            $mem[$start] = memory_get_usage();
        } else {
            // 计算区间时间或内存差值
            if (!isset($info[$end])) {
                $info[$end] = microtime(true);
            }
            if (!isset($mem[$end])) {
                $mem[$end] = memory_get_usage();
            }
            
            if ('m' == $dec) {
                // 返回内存使用差值（格式化为可读单位）
                $size = $mem[$end] - $mem[$start];
                $units = ['B', 'KB', 'MB', 'GB', 'TB'];
                $pos = 0;
                while ($size >= 1024 && $pos < count($units) - 1) {
                    $size /= 1024;
                    $pos++;
                }
                return round($size, 2) . ' ' . $units[$pos];
            } else {
                // 返回执行时间差值（秒）
                return number_format(($info[$end] - $info[$start]), $dec);
            }
        }
    }
}

// 新增：获取框架 session_id 的辅助函数，兼容 TP5 风格
if (!function_exists('get_session_id')) {
    /**
     * 获取框架 session_id
     * author: 王勇程 <laughskydragon@qq.com>
     */
    function get_session_id()
    {
        return \think\facade\Session::getId();
    }
}

// 新增：JWT Token 生成函数
